package database

import (
	"fmt"
	"strings"

	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

// TableColumnInfo 表字段信息
type TableColumnInfo struct {
	Field      string `gorm:"column:Field"`
	Type       string `gorm:"column:Type"`
	Null       string `gorm:"column:Null"`
	Key        string `gorm:"column:Key"`
	Default    string `gorm:"column:Default"`
	Extra      string `gorm:"column:Extra"`
	Comment    string `gorm:"column:Comment"`
	ColumnType string `gorm:"column:Column_type"`
}

// GetTableColumns 获取表的所有列信息
func GetTableColumns(db *gorm.DB, tableName string) ([]TableColumnInfo, error) {
	var columns []TableColumnInfo
	sql := fmt.Sprintf("DESCRIBE `%s`", tableName)
	if err := db.Raw(sql).Scan(&columns).Error; err != nil {
		return nil, fmt.Errorf("获取表 %s 的列信息失败: %w", tableName, err)
	}
	return columns, nil
}

// GetTableIndexes 获取表的所有索引信息
func GetTableIndexes(db *gorm.DB, tableName string) ([]map[string]interface{}, error) {
	var indexes []map[string]interface{}
	sql := fmt.Sprintf("SHOW INDEX FROM `%s`", tableName)
	if err := db.Raw(sql).Scan(&indexes).Error; err != nil {
		return nil, fmt.Errorf("获取表 %s 的索引信息失败: %w", tableName, err)
	}
	return indexes, nil
}

// GenerateAlterTableSQL 生成 ALTER TABLE SQL（对比模型和现有表结构）
func GenerateAlterTableSQL(db *gorm.DB, model interface{}) (string, error) {
	stmt := &gorm.Statement{DB: db}
	if err := stmt.Parse(model); err != nil {
		return "", fmt.Errorf("解析模型失败: %w", err)
	}

	// 获取表名
	tableName := stmt.Schema.Table
	if tableName == "" {
		if tn, ok := model.(interface{ TableName() string }); ok {
			tableName = tn.TableName()
		} else {
			return "", fmt.Errorf("无法获取表名")
		}
	}

	// 检查表是否存在
	exists, err := TableExists(tableName)
	if err != nil {
		return "", err
	}
	if !exists {
		// 表不存在，生成 CREATE TABLE
		return GenerateCreateTableSQL(db, model)
	}

	// 获取现有表的列信息
	existingColumns, err := GetTableColumns(db, tableName)
	if err != nil {
		return "", err
	}

	// 构建现有列的映射（列名 -> 列信息）
	existingColMap := make(map[string]TableColumnInfo)
	for _, col := range existingColumns {
		existingColMap[col.Field] = col
	}

	// 获取现有外键信息
	existingForeignKeys, _ := GetTableForeignKeys(db, tableName)

	// 生成 ALTER TABLE 语句
	var alterSQL []string
	var warnings []string

	// 分析模型的字段（支持字段重命名检测）
	modelColMap := make(map[string]bool)
	renameMap := make(map[string]string) // newName -> oldName

	if stmt.Schema != nil {
		for _, field := range stmt.Schema.Fields {
			colName := field.DBName
			if colName == "" {
				continue
			}

			modelColMap[colName] = true

			// 生成字段 SQL
			fieldSQL := generateFieldSQL(field)

			if existingCol, exists := existingColMap[colName]; exists {
				// 字段已存在，检查是否需要修改
				if needsModify(fieldSQL, existingCol) {
					alterSQL = append(alterSQL, fmt.Sprintf("    MODIFY COLUMN %s", fieldSQL))
				}
			} else {
				// 字段不存在，可能是新字段或重命名字段
				// 尝试检测重命名：查找类型相似且未被使用的字段
				if oldName := detectRenameCandidate(field, existingColMap, modelColMap); oldName != "" {
					renameMap[colName] = oldName
					modelColMap[oldName] = true // 标记旧字段已处理
					warnings = append(warnings, fmt.Sprintf("检测到字段重命名：%s -> %s（使用 CHANGE COLUMN 保留数据）", oldName, colName))
					alterSQL = append(alterSQL, fmt.Sprintf("    CHANGE COLUMN `%s` %s", oldName, fieldSQL))
				} else {
					// 新字段，需要添加
					afterCol := determineAfterColumn(field, stmt.Schema)
					if afterCol != "" && existingColMap[afterCol].Field != "" {
						alterSQL = append(alterSQL, fmt.Sprintf("    ADD COLUMN %s AFTER `%s`", fieldSQL, afterCol))
					} else {
						alterSQL = append(alterSQL, fmt.Sprintf("    ADD COLUMN %s", fieldSQL))
					}
				}
			}
		}
	}

	// 检查需要删除的字段（模型中不存在但表中存在，且未被重命名）
	for colName, colInfo := range existingColMap {
		if !modelColMap[colName] {
			// 排除系统字段和已被重命名的字段
			if colName != "id" || colInfo.Key != "PRI" {
				alterSQL = append(alterSQL, fmt.Sprintf("    DROP COLUMN `%s`", colName))
			}
		}
	}

	// 处理外键变更
	fkChanges := generateForeignKeyChanges(stmt, existingForeignKeys)
	alterSQL = append(alterSQL, fkChanges...)

	// 如果没有任何变更，返回空
	if len(alterSQL) == 0 {
		return "", fmt.Errorf("表结构没有变化")
	}

	// 构建完整的 ALTER TABLE SQL
	var sqlBuilder strings.Builder

	// 添加警告注释
	if len(warnings) > 0 {
		sqlBuilder.WriteString("-- ⚠️ 警告：以下变更需要特别注意\n")
		for _, warning := range warnings {
			sqlBuilder.WriteString(fmt.Sprintf("-- %s\n", warning))
		}
		sqlBuilder.WriteString("\n")
	}

	sqlBuilder.WriteString(fmt.Sprintf("ALTER TABLE `%s`\n", tableName))
	sqlBuilder.WriteString(strings.Join(alterSQL, ",\n"))
	sqlBuilder.WriteString(";")

	return sqlBuilder.String(), nil
}

// detectRenameCandidate 检测字段重命名候选（基于类型相似性）
func detectRenameCandidate(newField *schema.Field, existingColMap map[string]TableColumnInfo, modelColMap map[string]bool) string {
	newType := extractFieldTypeFromField(newField)

	// 查找类型相似且未被模型使用的字段
	for colName, colInfo := range existingColMap {
		// 跳过已被模型使用的字段
		if modelColMap[colName] {
			continue
		}

		existingType := normalizeType(colInfo.Type)
		if newType == existingType || areTypesCompatible(newType, existingType) {
			// 类型相似，可能是重命名
			return colName
		}
	}

	return ""
}

// extractFieldTypeFromField 从字段提取类型
func extractFieldTypeFromField(field *schema.Field) string {
	dataType := ""
	if field.TagSettings["TYPE"] != "" {
		dataType = field.TagSettings["TYPE"]
	} else if field.DataType != "" {
		dataType = string(field.DataType)
	}

	if idx := strings.Index(dataType, "("); idx > 0 {
		dataType = dataType[:idx]
	}
	return strings.ToUpper(strings.TrimSpace(dataType))
}

// areTypesCompatible 检查两个类型是否兼容（可用于重命名检测）
func areTypesCompatible(type1, type2 string) bool {
	type1 = normalizeType(type1)
	type2 = normalizeType(type2)

	if type1 == type2 {
		return true
	}

	// INT 系列兼容
	intTypes := []string{"TINYINT", "SMALLINT", "INT", "BIGINT"}
	if containsType(intTypes, type1) && containsType(intTypes, type2) {
		return true
	}

	// VARCHAR 系列兼容
	if strings.Contains(type1, "VARCHAR") && strings.Contains(type2, "VARCHAR") {
		return true
	}

	return false
}

// containsType 检查类型是否在列表中
func containsType(types []string, t string) bool {
	for _, typ := range types {
		if strings.Contains(t, typ) {
			return true
		}
	}
	return false
}

// ForeignKeyInfo 外键信息
type ForeignKeyInfo struct {
	ConstraintName       string `gorm:"column:CONSTRAINT_NAME"`
	ColumnName           string `gorm:"column:COLUMN_NAME"`
	ReferencedTableName  string `gorm:"column:REFERENCED_TABLE_NAME"`
	ReferencedColumnName string `gorm:"column:REFERENCED_COLUMN_NAME"`
	OnDelete             string `gorm:"column:DELETE_RULE"`
	OnUpdate             string `gorm:"column:UPDATE_RULE"`
}

// GetTableForeignKeys 获取表的所有外键信息
func GetTableForeignKeys(db *gorm.DB, tableName string) ([]ForeignKeyInfo, error) {
	var foreignKeys []ForeignKeyInfo
	sql := `
		SELECT 
			CONSTRAINT_NAME,
			COLUMN_NAME,
			REFERENCED_TABLE_NAME,
			REFERENCED_COLUMN_NAME,
			DELETE_RULE,
			UPDATE_RULE
		FROM information_schema.KEY_COLUMN_USAGE
		WHERE TABLE_SCHEMA = DATABASE()
			AND TABLE_NAME = ?
			AND REFERENCED_TABLE_NAME IS NOT NULL
	`
	if err := db.Raw(sql, tableName).Scan(&foreignKeys).Error; err != nil {
		return nil, fmt.Errorf("获取表 %s 的外键信息失败: %w", tableName, err)
	}
	return foreignKeys, nil
}

// generateForeignKeyChanges 生成外键变更 SQL
func generateForeignKeyChanges(stmt *gorm.Statement, existingForeignKeys []ForeignKeyInfo) []string {
	var changes []string

	if stmt.Schema == nil {
		return changes
	}

	// 收集模型中的外键定义（从 Relationships）
	modelForeignKeys := make(map[string]*ForeignKeyDefinition)

	// 检查 Relationships 是否存在且包含关系
	// Relationships 是结构体，直接访问 Relations
	relations := stmt.Schema.Relationships.Relations
	if len(relations) > 0 {
		for _, rel := range relations {
			// 支持 BelongsTo 和 HasOne 关系
			if rel.Type == schema.BelongsTo || rel.Type == schema.HasOne {
				if len(rel.References) > 0 && len(rel.References[0].ForeignKey.DBName) > 0 {
					fkColumn := rel.References[0].ForeignKey.DBName
					refTable := rel.FieldSchema.Table
					refColumn := rel.References[0].PrimaryKey.DBName

					if refTable == "" || refColumn == "" {
						continue
					}

					fkName := fmt.Sprintf("fk_%s_%s", stmt.Schema.Table, fkColumn)
					modelForeignKeys[fkName] = &ForeignKeyDefinition{
						ColumnName:       fkColumn,
						ReferencedTable:  refTable,
						ReferencedColumn: refColumn,
						OnDelete:         "RESTRICT", // 默认
						OnUpdate:         "RESTRICT", // 默认
					}
				}
			}
		}
	}

	// 查找需要添加的外键
	for fkName, fkDef := range modelForeignKeys {
		found := false
		for _, existingFK := range existingForeignKeys {
			if existingFK.ConstraintName == fkName || existingFK.ColumnName == fkDef.ColumnName {
				found = true
				// 检查是否需要修改（如 ON DELETE 动作变化）
				if existingFK.OnDelete != fkDef.OnDelete || existingFK.OnUpdate != fkDef.OnUpdate {
					changes = append(changes, fmt.Sprintf("    DROP FOREIGN KEY `%s`", existingFK.ConstraintName))
					changes = append(changes, generateAddForeignKeySQL(fkName, fkDef))
				}
				break
			}
		}
		if !found {
			changes = append(changes, generateAddForeignKeySQL(fkName, fkDef))
		}
	}

	// 查找需要删除的外键（模型中不存在但表中存在）
	modelFKColumns := make(map[string]bool)
	for _, fkDef := range modelForeignKeys {
		modelFKColumns[fkDef.ColumnName] = true
	}

	for _, existingFK := range existingForeignKeys {
		if !modelFKColumns[existingFK.ColumnName] {
			changes = append(changes, fmt.Sprintf("    DROP FOREIGN KEY `%s`", existingFK.ConstraintName))
		}
	}

	return changes
}

// ForeignKeyDefinition 外键定义
type ForeignKeyDefinition struct {
	ColumnName       string
	ReferencedTable  string
	ReferencedColumn string
	OnDelete         string
	OnUpdate         string
}

// generateAddForeignKeySQL 生成添加外键的 SQL
func generateAddForeignKeySQL(fkName string, fkDef *ForeignKeyDefinition) string {
	onDelete := fkDef.OnDelete
	if onDelete == "" {
		onDelete = "RESTRICT"
	}
	onUpdate := fkDef.OnUpdate
	if onUpdate == "" {
		onUpdate = "RESTRICT"
	}

	return fmt.Sprintf(
		"    ADD CONSTRAINT `%s` FOREIGN KEY (`%s`) REFERENCES `%s`(`%s`) ON DELETE %s ON UPDATE %s",
		fkName, fkDef.ColumnName, fkDef.ReferencedTable, fkDef.ReferencedColumn, onDelete, onUpdate,
	)
}

// needsModify 检查字段是否需要修改
func needsModify(newFieldSQL string, existingCol TableColumnInfo) bool {
	// 简单的比较：如果类型、NULL 约束等不同，则需要修改
	// 这里使用简单规则，实际可能需要更复杂的比较

	// 提取新字段的类型和约束
	newType := extractTypeFromSQL(newFieldSQL)
	existingType := normalizeType(existingCol.Type)

	// 比较类型
	if newType != existingType {
		return true
	}

	// 比较 NULL 约束
	newNullable := !strings.Contains(strings.ToUpper(newFieldSQL), "NOT NULL")
	existingNullable := existingCol.Null == "YES"
	if newNullable != existingNullable {
		return true
	}

	// 比较默认值
	newDefault := extractDefaultFromSQL(newFieldSQL)
	existingDefault := existingCol.Default
	if newDefault != existingDefault {
		return true
	}

	return false
}

// extractTypeFromSQL 从 SQL 中提取类型
func extractTypeFromSQL(sql string) string {
	// 简单提取：找到第二个单词（字段名后的类型）
	parts := strings.Fields(sql)
	if len(parts) < 2 {
		return ""
	}

	// 移除反引号，获取类型（可能包含长度，如 VARCHAR(50)）
	typeStr := strings.Trim(parts[1], "`")

	// 如果包含 NOT NULL 等，需要去除
	typeStr = strings.Fields(typeStr)[0]

	return strings.ToUpper(typeStr)
}

// normalizeType 规范化类型字符串以便比较
func normalizeType(typeStr string) string {
	typeStr = strings.ToUpper(typeStr)
	// 移除 UNSIGNED 等修饰符以便比较
	typeStr = strings.ReplaceAll(typeStr, "UNSIGNED", "")
	typeStr = strings.TrimSpace(typeStr)
	return typeStr
}

// extractDefaultFromSQL 从 SQL 中提取默认值
func extractDefaultFromSQL(sql string) string {
	if !strings.Contains(strings.ToUpper(sql), "DEFAULT") {
		return ""
	}

	// 简单提取，实际可能需要更复杂的解析
	parts := strings.Fields(sql)
	for i, part := range parts {
		if strings.ToUpper(part) == "DEFAULT" && i+1 < len(parts) {
			return parts[i+1]
		}
	}
	return ""
}

// determineAfterColumn 确定新字段应该添加在哪个字段之后
func determineAfterColumn(field *schema.Field, stmtSchema *schema.Schema) string {
	// 找到当前字段在模型中的位置
	var prevField *schema.Field
	for i, f := range stmtSchema.Fields {
		if f.DBName == field.DBName {
			if i > 0 {
				prevField = stmtSchema.Fields[i-1]
				break
			}
		}
	}

	if prevField != nil {
		return prevField.DBName
	}

	// 如果没有前一个字段，返回空（添加到末尾）
	return ""
}

// GenerateDropColumnSQL 生成删除字段的 SQL（用于回滚）
func GenerateDropColumnSQL(tableName string, columnName string) string {
	return fmt.Sprintf("ALTER TABLE `%s` DROP COLUMN `%s`;", tableName, columnName)
}

// GenerateAddColumnSQL 生成添加字段的 SQL（用于回滚）
func GenerateAddColumnSQL(tableName string, columnSQL string) string {
	return fmt.Sprintf("ALTER TABLE `%s` DROP COLUMN %s;", tableName, columnSQL)
}
